﻿<ComClass(ClsMath.ClassId, ClsMath.InterfaceId, ClsMath.EventsId)>
Public Class ClsMath


#Region "COM GUID"
    ' 这些 GUID 提供此类的 COM 标识 
    ' 及其 COM 接口。若更改它们，则现有的
    ' 客户端将不再能访问此类。
    Public Const ClassId As String = "4a2e195e-a7a8-4816-9609-39152dd8a4ef"
    Public Const InterfaceId As String = "c84017a9-5512-4acc-8999-1b0cd97203f3"
    Public Const EventsId As String = "fde3d74f-9434-4bcd-9c59-a866adcff893"
#End Region

    ' 可创建的 COM 类必须具有一个不带参数的 Public Sub New() 
    ' 否则， 将不会在 
    ' COM 注册表中注册此类，且无法通过
    ' CreateObject 创建此类。
    Public Sub New()
        MyBase.New()
    End Sub

#Region "属性"
    Public ReadOnly Property Pi As Double
        Get
            Return Math.PI
        End Get
    End Property

    Public ReadOnly Property TwoPi As Double
        Get
            Return 2 * Me.Pi
        End Get
    End Property

    Public ReadOnly Property HalfPi As Double
        Get
            Return Math.PI * 0.5
        End Get
    End Property

    Public ReadOnly Property ThreeHalfPis As Double
        Get
            Return Math.PI * 1.5
        End Get
    End Property


    Public ReadOnly Property Epsilon As Double
        Get
            Return 0.000000000001
        End Get
    End Property

#End Region

#Region " 数组操作"
    Public Sub DeleteArrayItemAtIndex(arr() As Object, index As Long)
        Dim arrlist = arr.ToList()
        arrlist.RemoveAt(index)
        arr = arrlist.ToArray()
    End Sub

    Public Sub InsertArrayItemAtIndex(arr() As Object, index As Long, newValue As Object)
        Dim arrlist = arr.ToList()
        arrlist.Insert(index, newValue)
        arr = arrlist.ToArray()
    End Sub
#End Region

#Region "三角转换"
    Public Function ArcCos(x As Double) As Double
        Return Math.Acos(x)
    End Function

    Public Function ArcSin(x As Double) As Double
        Return Math.Asin(x)
    End Function

    Public Function Rad2Degree(rad As Double) As Double
        Return (180 * rad) / Math.PI
    End Function

    Public Function Degree2Rad(deg As Double) As Double
        Degree2Rad = (deg * Math.PI) / 180.0#
    End Function
#End Region

#Region "数学方法"
    ''' <summary>
    ''' 判定double数等于0
    ''' </summary>
    ''' <param name="number">待判断的数字</param>
    ''' <param name="threshold">精度,默认为1E-8</param>
    ''' <returns>布尔类型</returns>
    Public Function IsZero(number As Double, Optional threshold As Double = 0.00000001) As Boolean
        Return Math.Abs(number) < threshold
    End Function

    ''' <summary>
    ''' 判定double数等于1
    ''' </summary>
    ''' <param name="number">待判断的数字</param>
    ''' <param name="threshold">精度,默认为1E-8</param>
    ''' <returns>布尔类型</returns>
    Public Function IsOne(number As Double, Optional threshold As Double = 0.00000001) As Boolean
        Return Math.Abs(number - 1.0) < threshold
    End Function


    ''' <summary>
    ''' 判定double数字相等
    ''' </summary>
    ''' <param name="a"></param>
    ''' <param name="b"></param>
    ''' <param name="threshold">精度,默认为1E-8</param>
    ''' <returns>布尔类型</returns>
    Public Function IsEqual(a As Double, b As Double, Optional threshold As Double = 0.00000001) As Boolean
        Return IsZero(a - b, threshold)
    End Function


    ''' <summary>
    ''' 求过三点的圆的圆弧的图
    ''' </summary>
    ''' <param name="mp"></param>
    ''' <param name="sp"></param>
    ''' <param name="ep"></param>
    ''' <returns></returns>
    Public Function GetArcBulgeValue(ByVal mp As ClsPoint2d, ByVal sp As ClsPoint2d, ByVal ep As ClsPoint2d) As Double
        Dim isCw As Integer = IsClockWise(mp, sp, ep)
        If (isCw > 0) Then
            Dim vcp2Sp As ClsVector2d = mp.GetVector2dTo(sp)
            Dim vcp2Ep As ClsVector2d = mp.GetVector2dTo(ep)
            Dim totalAngle As Double = (
                    (vcp2Ep.AngelToXAxis - vcp2Sp.AngelToXAxis) +
                    IIf((vcp2Sp.AngelToXAxis > vcp2Ep.AngelToXAxis), Math.PI * 2, 0))
            Return (Math.Tan((totalAngle * 0.25)) * IIf((isCw = 1), CDbl(-1), CDbl(1)))
        End If
        Return 0
    End Function

    ''' <summary>
    ''' 判定旋转方向
    ''' </summary>
    ''' <param name="checkPnt">待检验的点</param>
    ''' <param name="sp">直线的起点</param>
    ''' <param name="ep">直线的终点</param>
    ''' <returns>旋转方向枚举</returns>
    Public Function IsClockWise(ByVal checkPnt As ClsPoint2d, ByVal sp As ClsPoint2d, ByVal ep As ClsPoint2d) As 旋转方向
        If Not checkPnt.ThreePointsCollinear(sp, ep) Then
            Return IIf((sp.GetVector2dTo(ep).CrossProductWith(sp.GetVector2dTo(checkPnt)) > 0), 旋转方向.ClockWise, 旋转方向.CounterClockWise)
        End If
        Return 旋转方向.Parallel
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="sp"></param>
    ''' <param name="ep"></param>
    ''' <param name="bulgeValue"></param>
    ''' <returns></returns>
    Public Function GetArcCenter(ByVal sp As ClsPoint2d, ByVal ep As ClsPoint2d, ByVal bulgeValue As Double) As ClsPoint2d
        Dim rtnPnt As New ClsPoint2d
        If (Math.Abs(bulgeValue) > 0.00000001) Then
            Dim b As Double = (0.5 * ((1 / bulgeValue) - bulgeValue))
            Dim x1 As Double = (0.5 * ((sp.X + ep.X) - (b * (ep.Y - sp.Y))))
            Dim y1 As Double = (0.5 * ((sp.Y + ep.Y) + (b * (ep.X - sp.X))))
            rtnPnt = New ClsPoint2d() With {.X = x1, .Y = y1}
        End If
        Return rtnPnt
    End Function

    ''' <summary>
    ''' 凸度求弧长
    ''' </summary>
    ''' <param name="sp"></param>
    ''' <param name="ep"></param>
    ''' <param name="bulgeValue"></param>
    ''' <returns></returns>
    Public Function GetArcLength(ByVal sp As ClsPoint2d, ByVal ep As ClsPoint2d, ByVal bulgeValue As Double) As Double
        'https://www.cnblogs.com/JJBox/p/14300098.html
        Dim bowLength = sp.GetDistanceTo(ep)        '//弦长
        Dim bowLength2 = bowLength / 2            '//半弦
        Dim archHeight = Math.Abs(bulgeValue) * bowLength2 '//拱高==凸度*半弦
        '//根据三角函数 (弦长/2)²+(半径-拱高)²=半径²
        '//再根据:完全平方公式变形 :  (a+b)²=a²+2ab+b²、(a-b)²=a²-2ab+b²
        Dim r = (bowLength2 * bowLength2 + archHeight * archHeight) / (2 * archHeight) '//半径
        '//求圆心角:一半圆心角[(对边==半弦长,斜边==半径)]; *2就是完整圆心角
        Dim asin = Math.Asin(bowLength2 / r) * 2 '; //反正弦
        '//弧长公式: 弧长 = 绝对值(圆心弧度) * 半径...就是单位*比例..缩放过程
        Return asin * r
    End Function

#End Region



End Class


